package fileLoader;


import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
//import static java.nio.file.StandardWatchEventKind.*;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/**
 * Example to watch a directory (or tree) for changes to files.
 */

public class WatchDir{

    private static WatchService watcher;
    private static Map<WatchKey,Path> keys;
    private boolean recursive=true;
    private static boolean trace = false;
    private boolean stopped = false; 
	private static WatchDir serviceInstance = new WatchDir();
	private String username;
	private String ip = "";

    @SuppressWarnings("unchecked")
    static <T> WatchEvent<T> cast(WatchEvent<?> event) {
        return (WatchEvent<T>)event;
    }

  
  
    /**
     * Register the given directory with the WatchService
     */
    private static void register(Path dir) throws IOException {
     
        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        if (trace) {
            Path prev = keys.get(key);
            if (prev == null) {
                System.out.format("register: %s\n", dir);
            } else {
                if (!dir.equals(prev)) {
                    System.out.format("update: %s -> %s\n", prev, dir);
                }
            }
        }
        keys.put(key, dir);
    }

    /**
     * Register the given directory, and all its sub-directories, with the
     * WatchService.
     */
    private void registerAll(final Path start) throws IOException {
        // register directory and sub-directories
        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                throws IOException
            {
                register(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }

    /**
     * Creates a WatchService and registers the given directory
     */
    WatchDir() {
    	
    	try{
    	
    	
    	Path dir = Paths.get("C://dropbox");
        this.watcher = FileSystems.getDefault().newWatchService();
        this.keys = new HashMap<WatchKey,Path>();
       

        if (recursive) {
            System.out.format("Scanning %s ...\n", dir);
            registerAll(dir);
            System.out.println("Done.");
        } else {
            register(dir);
        }

        // enable trace after initial registration
        this.trace = true;
    	}
    	
    	catch(Exception e)
    	{
    		e.printStackTrace();
    	}
    }

    /**
     * Process all events for keys queued to the watcher
     */
    void processEvents() {
        while(!stopped) {
            // wait for key to be signalled
            WatchKey key;
            try {
                key = watcher.take();            
                
            } catch (InterruptedException x) {
                return;
            }
            Path dir = keys.get(key);
          
            if (dir == null) {
                System.err.println("WatchKey not recognized!!");
                continue;
            }

            for (WatchEvent<?> event: key.pollEvents()) {
            	if(stopped)
            		break;
                WatchEvent.Kind kind = event.kind();

                // TBD - provide example of how OVERFLOW event is handled
                if (kind == OVERFLOW) {
                    continue;
                }

                // Context for directory entry event is the file name of entry
                WatchEvent<Path> ev = cast(event);
                Path name = ev.context();
                Path child = dir.resolve(name);

                // print out event
                System.out.format("%s: %s\n", event.kind().name(), child);
                System.out.println();
                System.out.println("______________________");
                // if directory is created, and watching recursively, then
                // register it and its sub-directories
                if (recursive && (kind == ENTRY_CREATE)) {
                    try {
                        if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
                            registerAll(child);
                        }
                    } catch (IOException x) {
                        // ignore to keep sample readbale
                    }
                }
                
                if(kind==ENTRY_CREATE)
                {
                	//getting type of what was created
                	boolean is_dir=child.toFile().isDirectory();
                	 
                	//if it's a directory (that was created by us)
            		if(is_dir && !SharedSingelton.getInstace().shared)
            		{
            			ServerConnector.registerFolderInServer(child.toString(),username);
            			File[] files=child.toFile().listFiles();
            			
            			SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
                    	Session session = sessionFactory.openSession();

                    	//getting all file paths
                    	Query query = session.createQuery("select g.filePath from Tag g");
                    	List<String> list1 = query.list();
                    	
            			for(int i=0;i<files.length;i++)
            			{
            				
            				if(!list1.contains(files[i].getAbsolutePath()))
            				{
            					System.out.println("a file that hasn't been tagged yet");
            					//getting array of tags
            					String[] labels=new Test().dynamicLoadAndLabel(files[i].getAbsolutePath());   
            					DBTagsMgr.addTags(files[i].getAbsolutePath(), labels);
            					//uploading file to server
            					ServerConnector.postFile(files[i].getAbsolutePath());
            				}
            			}
            		}
            		//if it's a file(created by us)
            		else if(!SharedSingelton.getInstace().shared)
            		{         
            			//getting array of tags
            			String[] labels=new Test().dynamicLoadAndLabel(child.toString());         			
            			//adding them
            			DBTagsMgr.addTags(child.toString(),labels);
            			//uploading file to server
            			ServerConnector.postFile(child.toString());
            		}
            		//SharedSingelton.getInstace().shared=false;
    			                	
                }
                if(kind==ENTRY_DELETE)
                {
                	
                	boolean is_file=false;
                	SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
                	Session session = sessionFactory.openSession();

                	//getting all file paths
                	Query query = session.createQuery("select g.filePath from Tag g");
                	List<String> list1 = query.list();

                	for(int i=0;i<list1.size();i++)
                	{
                		//if this path appears in the data base, it's a file
                		if(list1.get(i).equals(child.toString()))
                			is_file=true;
                	}

                	if(!is_file)//directory deleted
                	{

                		query = session.createQuery("select g.filePath from Tag g");

                		List<String> list=query.list();

                		for(int i=0;i<list.size();i++)	
                		{
                			//if this file was in the deleted folder
                			if(list.get(i).substring(0,list.get(i).lastIndexOf("\\")).equals(child.toString()))
                			{
                				DBTagsMgr.deleteFile(list.get(i));
                			}           		
                		}

                		if(!SharedSingelton.getInstace().deleted_by_someone_else)
                		{
                			//deleting from server
                			ServerConnector.deleteFolderFromServer(child.toString());
                			SharedSingelton.getInstace().deleted_by_someone_else=false;
                		}
                	}
                	else//file
                	{
                		System.out.println("File deleted.");
                		String file_path=child.toString();
                		//deleting it from data base
                		DBTagsMgr.deleteFile(file_path);
                		if(!SharedSingelton.getInstace().deleted_by_someone_else)
                		{
                			//deleting from server
                			ServerConnector.deleteFileFromServer(file_path);
                			SharedSingelton.getInstace().deleted_by_someone_else=false;
                		}

                	}
                }
            }

            // reset key and remove from set if directory no longer accessible
            boolean valid = key.reset();
            if (!valid) {
                keys.remove(key);

                // all directories are inaccessible
                if (keys.isEmpty()) {
                    break;
                }
            }
     }
        
    }

    /**
     * function that starts listening to operating system and creates
     * connection with server
     */
    public void start() {
    	
    	//updating boolean
        stopped=false;

        //opening the file which includes the username and password
        File file = new File("C:\\dropbox_data\\dropbox_user_config.txt");        
        BufferedReader reader = null; 
        
        try { 
                   
        	reader = new BufferedReader(new FileReader(file)); 
        	String text=reader.readLine();
        	//reading the username from the file
        	username=text.substring(text.indexOf(':')+1);
        	reader.readLine();
        	ip = reader.readLine();
        	ServerConnector.ip = ip;
        	
        	
        	ServerConnector.username=username;
        } 
        catch(Exception e)
        {
        	
        }
                 
         //running new thread which creates connection with servlet and listens to updates
        new Thread(new Runnable(){

			@Override
			public void run() {
				// TODO Auto-generated method stub

				while(!stopped)
				{


					SharedSingelton.getInstace().shared=false;
					
					HttpClient httpclient = new DefaultHttpClient();
					httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);

					//request to connectServlet
					HttpPost request = new HttpPost("http://"+ip+":8080/ProjServer/connect");
					List<NameValuePair> postParameters = new ArrayList<NameValuePair>(); 

					//sending the user name
					postParameters.add(new BasicNameValuePair("username",username));
					UrlEncodedFormEntity formEntity;

					try {

						formEntity = new UrlEncodedFormEntity((List<? extends org.apache.http.NameValuePair>) postParameters);
						request.setEntity(formEntity);
						//executing request
						HttpResponse response = httpclient.execute(request);

						if(!stopped)
						{
							HttpEntity resEntity = response.getEntity();

							if (resEntity != null) {
								
								//getting server's response message
								String response_msg=EntityUtils.toString(resEntity);
								//finding the type of the message
								String type = response_msg.substring(0, response_msg.indexOf(':'));							
								
								//case we need to create a folder
								//_______________________________________________________________________________________________
								if(type.equals("folder")){

									SharedSingelton.getInstace().shared=true;

									//getting the folder name we need to create
									String[]commas_elems=response_msg.split(",");
									String folderName = commas_elems[0].split(":")[1];

									System.out.println("folder name to create is: "+folderName);
									new File("C://dropbox//"+folderName).mkdir();

									//for each file that should be in this folder, download it
									for(int i=1;i<commas_elems.length;i++)
									{
										String id_name[] = commas_elems[i].split("/");
										//downloading file from server
										ServerConnector.downloadFileFromServer(id_name[0], id_name[1], folderName);

									}
								}
								
								//case we need to create and download file
								//____________________________________________________________________________________________
								else if(type.equals("uploaded")){

									SharedSingelton.getInstace().shared=true;

									//getting the message after the type
									response_msg=response_msg.substring(response_msg.indexOf(':')+1);
									String[]commas_elems=response_msg.split(",");

									//finding the name and file id we should download
									String file_downloaded=commas_elems[0];
									String new_file_name=commas_elems[1];
									String folder_name=commas_elems[2];

									System.out.println(file_downloaded);
									System.out.println(new_file_name);

									//downloading file
									ServerConnector.downloadFileFromServer(file_downloaded, new_file_name, folder_name);

								}
								
								//we need to delete file
								//_________________________________________________________________________________________
								else if(type.equals("deleted_file"))
								{
									String[] data = response_msg.substring(response_msg.indexOf(':')+1).split(",");
									String filePath = "C://dropbox//"+data[1]+"//"+data[0];
									SharedSingelton.getInstace().deleted_by_someone_else=true;
									//deleting the file
									new File(filePath).delete();

								}
								
								
//								else if(type.equals("deleted_folder"))
//								{
//									String folder_name = response_msg.substring(response_msg.indexOf(':')+1);
//
//									//	File f = new File("C://dropbox//"+folder_name);
//									File f = new File(folder_name);
//									if( f.exists() ) {
//										File[] files = f.listFiles();
//										for(int i=0; i<files.length; i++) {
//
//											files[i].delete();
//											//DBTagsMgr.deleteFile(files[i].getAbsolutePath());
//
//										}
//									}
//									f.delete();
//
//								}
							}
							
							httpclient.getConnectionManager().shutdown();
							
						}
						else
						{
							httpclient.getConnectionManager().shutdown();
						}
					} catch (Exception e) {
						// TODO Auto-generated catch block

						e.printStackTrace();
					}

				}

			}	
        }).start();
    
        	serviceInstance.processEvents();

    }
    
    
    /**
     * 
     * @param args- if args is start, going to start method, else to stop method
     */
	public static void windowsService(String args[]) {
	SharedSingelton.getInstace();
		String cmd = "start";
		if(args.length > 0) cmd = args[0];

		if("start".equals(cmd))  
			serviceInstance.start();
		else serviceInstance.stop();
	}
    
	/**
	 * method for stopping the service
	 */
	   public void stop() {
		   
		   //updaing boolean
		   stopped = true;

		   HttpClient httpclient = new DefaultHttpClient();
		   httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
		  
		   //going to disconnectServlet
		   HttpPost request = new HttpPost("http://"+ip+":8080/ProjServer/disconnect");

		   List<NameValuePair> postParameters = new ArrayList<NameValuePair>();   
		   BasicNameValuePair g; 
		   //adding username so the servlet would know which continuation object to remove
		   postParameters.add(new BasicNameValuePair("username",username));
		   UrlEncodedFormEntity formEntity;
		     
		    try {

		    	formEntity = new UrlEncodedFormEntity((List<? extends org.apache.http.NameValuePair>) postParameters);
		    	request.setEntity(formEntity);
		    	//executing the request
		    	HttpResponse response = httpclient.execute(request);		    	
		    	httpclient.getConnectionManager().shutdown();
		    	watcher.close();	    	
		    	
		    }
		    catch(Exception e)
		    {
		    	

		    }
		   }
  
}